﻿#include <iostream>
#include <string>
#include <cmath>
#include <unordered_map>
using namespace std;

// 靶面尺寸计算
// 业界通用的规范就是 1英吋 CCD尺寸= 长 12.8mm × 宽 9.6mm = 对角线为 16mm 之对应面积，该三角之三边比例为 4：3：5
// 靶面尺寸和镜头焦距配套对应的视场角: https://blog.csdn.net/dafenqie/article/details/78393412
// 相机芯片尺寸及选型公式: https://wenku.baidu.com/view/be59fa8759eef8c75fbfb3d3.html
// 工业CCD相机芯片靶面尺寸的计算: https://blog.csdn.net/yu_fujiang/article/details/51729368

// 相机、镜头靶面尺寸
class SensorSize
{
public:
    SensorSize(double w, double h, double diagonal)
    : m_width(w), m_height(h), m_diagonal(diagonal)
    { }

    SensorSize(const SensorSize& other)
    { *this = other; }

    SensorSize& operator = (const SensorSize& other)
    {
        if(this == &other)
            return *this;
        
        this->m_width = other.m_width;
        this->m_height = other.m_height;
        this->m_diagonal = other.m_diagonal;
        return *this;
    }

    inline double getWidth() const { return m_width; }
    inline double getHeight() const { return m_height; }
    inline double getDiagonal() const { return m_diagonal; }

private:
    double m_width; // 宽
    double m_height; // 高
    double m_diagonal; // 对角线
};

ostream& operator << (ostream& out, const SensorSize& s)
{
    return out << "SensorSize: w= " << s.getWidth() 
        << ",\th= " << s.getHeight() 
        << ",\tdiagonal= " << s.getDiagonal() 
        << endl;
}


// 相机选型
class CameraSelector
{
public:
    CameraSelector(SensorSize& camera, SensorSize& camlens)
    : m_camera(camera), m_camlens(camlens)
    { }

public:
    inline void CalcHeight(double hAov, double width)
    {
        m_hAov = hAov;
        m_width = width;

        // 计算高度
        double w = m_width * m_camlens.getWidth() / m_camera.getWidth();
        cout << "w=" << w << endl;
        m_height = w * 0.5 / tan(degToArc(m_hAov * 0.5));
        cout << "hAov= " << m_hAov << ", width= " << m_width << ", height= " << m_height << endl;
    }

    inline void CalcHAov(double width, double height)
    {
        m_width = width;
        m_height = height;

        // 计算视场角
        double w = m_width * m_camlens.getWidth() / m_camera.getWidth();
        cout << "w=" << w << endl;
        m_hAov = arcToDeg(atan(w * 0.5 / m_height)) * 2;
        cout << "width= " << m_width << ", height= " << m_height << ", hAov= " << m_hAov << endl;
    }

private:
    const double PI = 3.1415926;
    double arcToDeg(const double arc) { return arc * 180.0 / PI; }
    double degToArc(const double deg) { return deg * PI / 180.0; }

private:
    double m_hAov; // 视场角
    double m_height; // 高度（物距）
    double m_width; // 最大宽度
    SensorSize m_camera; // 相机靶面尺寸
    SensorSize m_camlens; // 镜头靶面尺寸
};

int main()
{
    unordered_map<string, SensorSize> m_sensor;
    // m_sensor["1"] = SensorSize(12.8, 9.6, 16.); // ? 这样赋值存在问题，编译不通过
    m_sensor.insert(make_pair<string, SensorSize>("1.1",    SensorSize(12.0, 12.0, 17.0)));
    m_sensor.insert(make_pair<string, SensorSize>("1",      SensorSize(12.8, 9.6, 16.)));
    m_sensor.insert(make_pair<string, SensorSize>("2/3",    SensorSize(8.8, 6.6, 11.)));
    m_sensor.insert(make_pair<string, SensorSize>("1/1.6",  SensorSize(8.08, 6.01, 10.07)));
    m_sensor.insert(make_pair<string, SensorSize>("1/1.7",  SensorSize(7.6, 5.7, 9.5)));
    m_sensor.insert(make_pair<string, SensorSize>("1/1.8",  SensorSize(7.2, 5.4, 9.)));
    m_sensor.insert(make_pair<string, SensorSize>("1/2",    SensorSize(6.4, 4.8, 8.)));
    m_sensor.insert(make_pair<string, SensorSize>("1/2.3",  SensorSize(6.16, 4.62, 7.7)));
    m_sensor.insert(make_pair<string, SensorSize>("1/2.5",  SensorSize(5.76, 4.29, 7.182)));
    m_sensor.insert(make_pair<string, SensorSize>("1/3",    SensorSize(4.8, 3.6, 6.)));
    m_sensor.insert(make_pair<string, SensorSize>("1/3.6",  SensorSize(4., 3., 5.)));
    m_sensor.insert(make_pair<string, SensorSize>("1/4",    SensorSize(3.2, 2.4, 4.)));

    for(auto sensor : m_sensor)
        cout << sensor.first << " \t---- "<< sensor.second;

    int select;
    cout << "根据宽度和视场角计算高度，输入1" << endl;
    cout << "根据宽度和高度计算视场角，输入2" << endl;
    cout << "请输入选择："; cin >> select;

    if(select == 1)
    {
        string camsize, lenssize;
        double hAov, width;
        cout << "求解相机安装高度.........." << endl;
        cout << "输入相机靶面尺寸: ";   cin >> camsize;
        cout << "输入镜头靶面尺寸: ";   cin >> lenssize;
        cout << "输入宽度视场角: ";     cin >> hAov;
        cout << "输入球桌宽度: ";       cin >> width;

        SensorSize camera = m_sensor.at(camsize);
        SensorSize camlens = m_sensor.at(lenssize);
        CameraSelector sel(camera, camlens);
        sel.CalcHeight(hAov, width);
    }
    else if(select == 2)
    {
        string camsize, lenssize;
        double width, height;
        cout << "求解水平视场角.........." << endl;
        cout << "输入相机靶面尺寸: ";       cin >> camsize;
        cout << "输入镜头靶面尺寸: ";       cin >> lenssize;
        cout << "输入球桌宽度: ";           cin >> width;
        cout << "输入相机安装高度: ";     cin >> height;

        SensorSize camera = m_sensor.at(camsize);
        SensorSize camlens = m_sensor.at(lenssize);
        CameraSelector sel(camera, camlens);
        sel.CalcHAov(width, height);
    }
}
